/**
 * PANDA 3D SOFTWARE
 * Copyright (c) Carnegie Mellon University.  All rights reserved.
 *
 * All use of this software is subject to the terms of the revised BSD
 * license.  You should have received a copy of this license along
 * with this source code in a file named "LICENSE."
 *
 * @file collisionEntry.I
 * @author drose
 * @date 2002-03-16
 */

/**
 *
 */
INLINE CollisionEntry::
CollisionEntry() {
  _flags = 0;
  // > 1. means collision didn't happen
  _t = 2.f;
}

/**
 * Returns the CollisionSolid pointer for the particular solid that triggered
 * this collision.
 */
INLINE const CollisionSolid *CollisionEntry::
get_from() const {
  return _from;
}

/**
 * Returns true if the "into" solid is, in fact, a CollisionSolid, and its
 * pointer is known (in which case get_into() may be called to retrieve it).
 * If this returns false, the collision was detected into a GeomNode, and
 * there is no CollisionSolid pointer to be retrieved.
 */
INLINE bool CollisionEntry::
has_into() const {
  return (_into != nullptr);
}

/**
 * Returns the CollisionSolid pointer for the particular solid was collided
 * into.  This pointer might be NULL if the collision was into a piece of
 * visible geometry, instead of a normal CollisionSolid collision; see
 * has_into().
 */
INLINE const CollisionSolid *CollisionEntry::
get_into() const {
  return _into;
}

/**
 * Returns the node that contains the CollisionSolid that triggered this
 * collision.  This will be a node that has been added to a CollisionTraverser
 * via add_collider().
 */
INLINE CollisionNode *CollisionEntry::
get_from_node() const {
  return _from_node;
}

/**
 * Returns the node that contains the CollisionSolid that was collided into.
 * This returns a PandaNode pointer instead of something more specific,
 * because it might be either a CollisionNode or a GeomNode.
 *
 * Also see get_into_node_path().
 */
INLINE PandaNode *CollisionEntry::
get_into_node() const {
  return _into_node;
}

/**
 * Returns the NodePath that represents the CollisionNode that contains the
 * CollisionSolid that triggered this collision.  This will be a NodePath that
 * has been added to a CollisionTraverser via add_collider().
 */
INLINE NodePath CollisionEntry::
get_from_node_path() const {
  return _from_node_path;
}

/**
 * Returns the NodePath that represents the specific CollisionNode or GeomNode
 * instance that was collided into.  This is the same node returned by
 * get_into_node(), represented as a NodePath; however, it may be more useful
 * because the NodePath can resolve the particular instance of the node, if
 * there is more than one.
 */
INLINE NodePath CollisionEntry::
get_into_node_path() const {
  return _into_node_path;
}

/**
 * Sets a time value for this collision relative to other CollisionEntries
 */
INLINE void CollisionEntry::
set_t(PN_stdfloat t) {
  nassertv(!cnan(t));
  _t = t;
}

/**
 * returns time value for this collision relative to other CollisionEntries
 */
INLINE PN_stdfloat CollisionEntry::
get_t() const {
  return _t;
}

/**
 * returns true if this represents an actual collision as opposed to a
 * potential collision, needed for iterative collision resolution where path
 * of collider changes mid-frame
 */
INLINE bool CollisionEntry::
collided() const {
  return ((0.f <= _t) && (_t <= 1.f));
}

/**
 * prepare for another collision test
 */
INLINE void CollisionEntry::
reset_collided() {
  _t = 2.f;
}

/**
 * Returns true if the collision was detected by a CollisionTraverser whose
 * respect_prev_transform flag was set true, meaning we should consider motion
 * significant in evaluating collisions.
 */
INLINE bool CollisionEntry::
get_respect_prev_transform() const {
  return (_flags & F_respect_prev_transform) != 0;
}


/**
 * Stores the point, on the surface of the "into" object, at which a collision
 * is detected.
 *
 * This point is specified in the coordinate space of the "into" object.
 */
INLINE void CollisionEntry::
set_surface_point(const LPoint3 &point) {
  nassertv(!point.is_nan());
  _surface_point = point;
  _flags |= F_has_surface_point;
}

/**
 * Stores the surface normal of the "into" object at the point of the
 * intersection.
 *
 * This normal is specified in the coordinate space of the "into" object.
 */
INLINE void CollisionEntry::
set_surface_normal(const LVector3 &normal) {
  nassertv(!normal.is_nan());
  _surface_normal = normal;
  _flags |= F_has_surface_normal;
}

/**
 * Stores the point, within the interior of the "into" object, which
 * represents the depth to which the "from" object has penetrated.  This can
 * also be described as the intersection point on the surface of the "from"
 * object (which is inside the "into" object).
 *
 * This point is specified in the coordinate space of the "into" object.
 */
INLINE void CollisionEntry::
set_interior_point(const LPoint3 &point) {
  nassertv(!point.is_nan());
  _interior_point = point;
  _flags |= F_has_interior_point;
}

/**
 * Returns true if the surface point has been specified, false otherwise.  See
 * get_surface_point().  Some types of collisions may not compute the surface
 * point.
 */
INLINE bool CollisionEntry::
has_surface_point() const {
  return (_flags & F_has_surface_point) != 0;
}

/**
 * Returns true if the surface normal has been specified, false otherwise.
 * See get_surface_normal().  Some types of collisions may not compute the
 * surface normal.
 */
INLINE bool CollisionEntry::
has_surface_normal() const {
  return (_flags & F_has_surface_normal) != 0;
}

/**
 * Returns true if the interior point has been specified, false otherwise.
 * See get_interior_point().  Some types of collisions may not compute the
 * interior point.
 */
INLINE bool CollisionEntry::
has_interior_point() const {
  return (_flags & F_has_interior_point) != 0;
}

/**
 * Stores the position of the "from" object at the instant at which the
 * collision is first detected.
 *
 * This position is specified in the coordinate space of the "into" object.
 */
INLINE void CollisionEntry::
set_contact_pos(const LPoint3 &pos) {
  nassertv(!pos.is_nan());
  _contact_pos = pos;
  _flags |= F_has_contact_pos;
}

/**
 * Stores the surface normal of the "into" object at the contact pos.
 *
 * This normal is specified in the coordinate space of the "into" object.
 */
INLINE void CollisionEntry::
set_contact_normal(const LVector3 &normal) {
  nassertv(!normal.is_nan());
  _contact_normal = normal;
  _flags |= F_has_contact_normal;
}

/**
 * Returns true if the contact position has been specified, false otherwise.
 * See get_contact_pos().  Some types of collisions may not compute the
 * contact pos.
 */
INLINE bool CollisionEntry::
has_contact_pos() const {
  return (_flags & F_has_contact_pos) != 0;
}

/**
 * Returns true if the contact normal has been specified, false otherwise.
 * See get_contact_normal().  Some types of collisions may not compute the
 * contact normal.
 */
INLINE bool CollisionEntry::
has_contact_normal() const {
  return (_flags & F_has_contact_normal) != 0;
}

/**
 * Returns the relative transform of the from node as seen from the into node.
 */
INLINE CPT(TransformState) CollisionEntry::
get_wrt_space() const {
  return _from_node_path.get_transform(_into_node_path);
}

/**
 * Returns the relative transform of the into node as seen from the from node.
 */
INLINE CPT(TransformState) CollisionEntry::
get_inv_wrt_space() const {
  return _into_node_path.get_transform(_from_node_path);
}

/**
 * Returns the relative transform of the from node as seen from the into node,
 * as of the previous frame (according to set_prev_transform(),
 * set_fluid_pos(), etc.)
 */
INLINE CPT(TransformState) CollisionEntry::
get_wrt_prev_space() const {
  if (get_respect_prev_transform()) {
    return _from_node_path.get_prev_transform(_into_node_path);
  } else {
    return get_wrt_space();
  }
}

/**
 * Returns the relative transform of the from node as seen from the into node.
 */
INLINE LMatrix4 CollisionEntry::
get_wrt_mat() const {
  return get_wrt_space()->get_mat();
}

/**
 * Returns the relative transform of the into node as seen from the from node.
 */
INLINE LMatrix4 CollisionEntry::
get_inv_wrt_mat() const {
  return get_inv_wrt_space()->get_mat();
}

/**
 * Returns the relative transform of the from node as seen from the into node,
 * as of the previous frame (according to set_prev_transform(),
 * set_fluid_pos(), etc.)
 */
INLINE LMatrix4 CollisionEntry::
get_wrt_prev_mat() const {
  return get_wrt_prev_space()->get_mat();
}

/**
 * Returns the ClipPlaneAttrib, if any, that is applied to the into_node_path,
 * or NULL if there is no clip plane in effect.
 */
INLINE const ClipPlaneAttrib *CollisionEntry::
get_into_clip_planes() const {
  if ((_flags & F_checked_clip_planes) == 0) {
    ((CollisionEntry *)this)->check_clip_planes();
  }
  return _into_clip_planes;
}

/**
 * This is intended to be called only by the CollisionTraverser.  It requests
 * the CollisionEntry to start the intersection test between the from and into
 * solids stored within it, passing the result (if positive) to the indicated
 * CollisionHandler.
 */
INLINE void CollisionEntry::
test_intersection(CollisionHandler *record,
                  const CollisionTraverser *trav) const {
  PT(CollisionEntry) result = get_from()->test_intersection(*this);
#ifdef DO_COLLISION_RECORDING
  if (trav->has_recorder()) {
    if (result != nullptr) {
      trav->get_recorder()->collision_tested(*result, true);
    } else {
      trav->get_recorder()->collision_tested(*this, false);
    }
  }
#endif  // DO_COLLISION_RECORDING
#ifdef DO_PSTATS
  ((CollisionSolid *)get_into())->get_test_pcollector().add_level(1);
#endif  // DO_PSTATS
  // if there was no collision detected but the handler wants to know about
  // all potential collisions, create a "didn't collide" collision entry for
  // it
  if (record->wants_all_potential_collidees() && result == nullptr) {
    result = new CollisionEntry(*this);
    result->reset_collided();
  }
  if (result != nullptr) {
    record->add_entry(result);
  }
}

INLINE std::ostream &
operator << (std::ostream &out, const CollisionEntry &entry) {
  entry.output(out);
  return out;
}
